﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.ServiceModel.Routing;
using EasyComm.Contracts;
using EasyComm.Discovery;
using EasyComm.ServiceUnit;
using EasyComm.Utilities;

namespace EasyComm.Hosting.Administration
{
    public class NLBAdminService : IAdminNLBContract
    {
        private NLBProxyServiceHost _proxyHost;
        private NLBRouterServiceHost _routerHost;
        private List<Type> _serviceContractList = new List<Type>();

        public WorkerServiceUnitCollection WorkerServiceList { get; private set; }
        public AdminServiceUnitCollection<IAdminSLBContract> SLBAdminServiceList { get; private set; }

        internal WorkerServiceUnitCollection _workerServiceList { get; private set; }
        internal AdminServiceUnitCollection<IAdminSLBContract> _slbAdminServiceList { get; private set; }

        public NLBAdminService()
        {
            _workerServiceList = new WorkerServiceUnitCollection();
            _workerServiceList.Change += new ThreadSafeCollectionChangeEventHandler<WorkerServiceUnit>(_workerServiceList_Change);
            _slbAdminServiceList = new AdminServiceUnitCollection<IAdminSLBContract>();
            _slbAdminServiceList.Change += new ThreadSafeCollectionChangeEventHandler<AdminServiceUnit<IAdminSLBContract>>(_slbAdminServiceList_Change);

            WorkerServiceList = new WorkerServiceUnitCollection();
            SLBAdminServiceList = new AdminServiceUnitCollection<IAdminSLBContract>();
        }

        void _slbAdminServiceList_Change(object sender, ThreadSafeCollectionEventArgs<AdminServiceUnit<IAdminSLBContract>> e)
        {
            switch (e.ChangeType)
            {
                case ChangeType.NotChanged:
                    break;
                case ChangeType.Added:
                    SLBAdminServiceList.Insert(e.Index, e.Item);
                    break;
                case ChangeType.Removed:
                    SLBAdminServiceList.Remove(e.Item);
                    break;
                case ChangeType.Modified:
                    SLBAdminServiceList[e.Index] = e.Item;
                    break;
                case ChangeType.RemovedAll:
                    SLBAdminServiceList.Clear();
                    break;
                default:
                    break;
            }
        }

        void _workerServiceList_Change(object sender, ThreadSafeCollectionEventArgs<WorkerServiceUnit> e)
        {
            throw new NotImplementedException();
        }

        public ServiceResponse Initialize()
        {
            try
            {
                string contractFolderPath = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Contracts");
                if (!Directory.Exists(contractFolderPath))
                {
                    return new ServiceResponse(1, string.Format("No contracts found on '{0}'.", contractFolderPath), MessageType.Warn);
                }
                string[] contractAssemblyList = Directory.GetFiles(contractFolderPath, "*.dll");
                foreach (string filePath in contractAssemblyList)
                {
                    List<Type> contracts = ReflectionContext.FindContractInterfaces(filePath);
                    if (contracts.Count > 0)
                    {
                        _serviceContractList.AddRange(contracts);
                    }
                }
                if (_serviceContractList.Count > 0)
                {
                    return RestartServices();
                }
                else
                {
                    return new ServiceResponse(1, "Assembly does not implement any contracts.", MessageType.Warn);
                }
            }
            catch (Exception ex)
            {
                return new ServiceResponse(2, ex.ToString(), MessageType.Error);
            }
        }

        public ServiceResponse InitializeContracts(Type[] serviceContractList)
        {
            try
            {
                if (serviceContractList != null && serviceContractList.Length > 0)
                {
                    _serviceContractList.AddRange(serviceContractList);
                }
                else
                {
                    return new ServiceResponse(2, "serviceContractList is null or not an object!", MessageType.Error);
                }
                return RestartServices();
            }
            catch (Exception ex)
            {
                return new ServiceResponse(2, ex.ToString(), MessageType.Error);
            }
        }

        public SystemServiceUnit GetSystemService(SystemServiceType serviceType)
        {
            SystemServiceUnit service = null;
            switch (serviceType)
            {
                case SystemServiceType.NLBProxy:
                    if (_proxyHost != null)
                    {
                        service = new SystemServiceUnit(_proxyHost) { Type = serviceType };
                        if (_proxyHost.State == System.ServiceModel.CommunicationState.Opened)
                        {
                            service.Status = ServiceStatus.Online;
                        }
                        else
                        {
                            service.Status = ServiceStatus.Offline;
                        }
                    }
                    break;
                case SystemServiceType.NLBRouter:
                    if (_routerHost != null)
                    {
                        service = new SystemServiceUnit(_routerHost);
                        if (_routerHost.State == System.ServiceModel.CommunicationState.Opened)
                        {
                            service.Status = ServiceStatus.Online;
                        }
                        else
                        {
                            service.Status = ServiceStatus.Offline;
                        }
                    }
                    break;
            }
            return service;
        }

        public SystemServiceUnitCollection GetSystemServiceList()
        {
            SystemServiceUnitCollection serviceList = new SystemServiceUnitCollection();
            serviceList.Add(GetSystemService(SystemServiceType.NLBProxy));
            serviceList.Add(GetSystemService(SystemServiceType.NLBRouter));
            return serviceList;
        }

        public ServiceResponse LoadWorkerServiceAssembly(byte[] file, string fileName)
        {
            try
            {
                string filePath = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Contracts", fileName);
                FileUtils.SaveFile(file, filePath);
                List<Type> contracts = ReflectionContext.FindContractInterfaces(filePath);
                if (contracts.Count > 0)
                {
                    _serviceContractList.AddRange(contracts);
                }
                else
                {
                    return new ServiceResponse(1, "Assembly does not implement any contracts.", MessageType.Warn);
                }
                return RestartServices();
            }
            catch (Exception ex)
            {
                return new ServiceResponse(2, ex.ToString(), MessageType.Error);
            }
        }

        private ServiceResponse StopProxyService()
        {
            try
            {
                if (_proxyHost != null)
                {
                    _proxyHost.Close();
                    _proxyHost = null;
                }
                Trace.WriteLine("NLB DiscoveryProxy stopped.");
                Trace.WriteLine("");
                return new ServiceResponse();
            }
            catch (Exception ex)
            {
                return new ServiceResponse(2, ex.ToString(), MessageType.Error);
            }
        }

        private ServiceResponse StopRouterService()
        {
            try
            {
                if (_routerHost != null)
                {
                    _routerHost.Close();
                    _routerHost = null;
                }
                Trace.WriteLine("NLB DiscoveryRouter stopped.");
                Trace.WriteLine("");
                return new ServiceResponse();
            }
            catch (Exception ex)
            {
                return new ServiceResponse(2, ex.ToString(), MessageType.Error);
            }
        }

        private ServiceResponse SetRouterService()
        {
            try
            {
                if (_serviceContractList == null || _serviceContractList.Count == 0)
                {
                    return new ServiceResponse(1, "NLB Router does is not aware of any implemented contracts.", MessageType.Warn);
                }
                if (_routerHost == null)
                {
                    _routerHost = new NLBRouterServiceHost(this, typeof(RoutingService), _serviceContractList.ToArray(), new Uri[] { _proxyHost.Contracts[0].ListenUri });
                    _routerHost.Open();
                }
                Trace.WriteLine(string.Format("NLB DiscoveryRouter listening on '{0}'...", _routerHost.Contracts[0].ListenUri));
                Trace.WriteLine("");

                return new ServiceResponse();
            }
            catch (Exception ex)
            {
                return new ServiceResponse(2, ex.ToString(), MessageType.Error);
            }
        }

        private ServiceResponse SetProxyService()
        {
            try
            {
                if (_serviceContractList == null || _serviceContractList.Count == 0)
                {
                    return new ServiceResponse(1, "NLB Proxy does is not aware of any implemented contracts.", MessageType.Warn);
                }
                if (_proxyHost == null)
                {
                    _proxyHost = new NLBProxyServiceHost(this, typeof(DiscoveryProxyService), _serviceContractList.ToArray());
                    _proxyHost.Open();
                }
                Trace.WriteLine(string.Format("NLB DiscoveryProxy listening on '{0}'...", _proxyHost.Contracts[0].ListenUri));
                Trace.WriteLine("");
                return new ServiceResponse();
            }
            catch (Exception ex)
            {
                return new ServiceResponse(2, ex.ToString(), MessageType.Error);
            }
        }

        private ServiceResponse RestartServices()
        {
            ServiceResponse response = StopRouterService();
            if (response.Code != 0)
            {
                return response;
            }
            response = StopProxyService();
            if (response.Code != 0)
            {
                return response;
            }
            response = SetProxyService();
            if (response.Code != 0)
            {
                return response;
            }
            return SetRouterService();
        }
    }
}
